/* eslint-disable @typescript-eslint/no-explicit-any */

import { genId } from '../../tools/utils';
import { Empty } from '../empty/empty';
import { GridBase, type GridBaseData, type GridBaseProps, genWidth } from '../gridBase';
import { DATA } from '../symbol';
import { tableStyle } from './table.css';

export type TableData = GridBaseData & { id: string };

export class Table<
	P extends GridBaseProps<C> = GridBaseProps<Table<any, any>>,
	C extends Table<any, any> = Table<any, any>,
> extends GridBase<P, C> {
	protected get data(): TableData {
		return this[DATA] as TableData;
	}

	protected render(): Node {
		const id = `tb-${genId()}`;
		this.data.id = id;
		return (
			<div class={tableStyle}>
				<style></style>
				<table id={id}>
					<colgroup></colgroup>
					<thead></thead>
					<tbody></tbody>
				</table>
			</div>
		);
	}
	constructor(props: P) {
		super(props);
		super.init();
		this.setHeadLine(true).setShowEmpty(true).setColumnSize('auto');
	}

	protected override setActiveRow(rowIdx: number | null, focus: boolean): void {
		this.el
			.querySelectorAll(':scope>table>tbody>tr')
			.forEach((tr) => tr.classList.remove('active'));
		if (rowIdx !== null) {
			const tr = this.el.querySelector<HTMLTableRowElement>(`tbody>tr.r${rowIdx}`);
			if (!tr) {
				return;
			}
			tr.classList.add('active');
			if (focus) {
				const headerHeight =
					this.el.querySelector<HTMLTableSectionElement>(':scope>table>thead')?.offsetHeight ?? 0;
				if (tr.offsetTop + tr.offsetHeight > this.el.scrollTop + this.el.clientHeight) {
					// after the viewport.
					this.el.scrollTop = tr.offsetTop + tr.offsetHeight - this.el.clientHeight;
				}
				if (tr.offsetTop < this.el.scrollTop + headerHeight) {
					// before the viewport.
					this.el.scrollTop = tr.offsetTop - headerHeight;
				}
			}
		}
		this[DATA].actived = rowIdx;
	}

	protected override renderSelect() {
		const columns = this.getColumns();
		const th = this.el.querySelector(':scope>table>thead>tr.head>th.c0');
		if (th) {
			const contents = columns[0].formatter?.({}, null, -1, 0);
			if (Array.isArray(contents)) {
				th.replaceChildren(...contents);
			} else {
				th.replaceChildren(contents ?? '');
			}
		}
		const tds = this.el.querySelectorAll(`:scope>table>tbody>tr>td.c0`);
		for (let i = 0; i < tds.length; i++) {
			const td = tds[i];
			const contents = columns[0].formatter?.({}, null, i, 0);
			if (Array.isArray(contents)) {
				td.replaceChildren(...contents);
			} else {
				td.replaceChildren(contents ?? '');
			}
		}
	}

	protected override genStyle() {
		const columns = this.adjustColumnSize();
		const cols = this.el.querySelectorAll<HTMLTableColElement>(':scope>table>colgroup>col');
		cols.forEach((col, i) => {
			const c = columns[i];
			const width = genWidth(c.width);
			col.style.width = width;
		});
		// set fixed columns;
		let text = '';
		const rulerCells = this.el.querySelectorAll(':scope>table>thead>tr>td');
		for (let i = 0; i < this.fixColumns && i < columns.length; i++) {
			text += `.${tableStyle} table.${this.data.id} .c${i}{z-index:1;position:sticky;left:${
				(rulerCells[i] as HTMLElement).offsetLeft
			}px;}`;
		}
		const style = this.el.querySelector(':scope>style');
		if (style) style.innerHTML = text;
		return false;
	}

	protected override renderBody() {
		const columns = this.getColumns();
		const lastCol = columns[columns.length - 1];
		const tbody = this.el.querySelector(':scope>table>tbody');
		if (!tbody) {
			return;
		}
		this.el.querySelector(':scope>div.empty')?.remove();
		const data = this.sortedData;
		if (data.length > 0) {
			tbody.replaceChildren(
				...data.map((row, rowIdx) => {
					const key = this[DATA].section?.key;
					let sectionValue: unknown;
					if (key !== undefined) {
						sectionValue = (row as any)[key];
					}
					return (
						<tr
							class={[
								`r${rowIdx}`,
								rowIdx === this.active ? 'active' : '',
								this.isRowDisable(row) ? 'disable' : '',
							]}
						>
							{sectionValue && lastCol
								? this.renderSection(
										(<td colSpan={columns.length}></td>) as HTMLTableCellElement,
										sectionValue,
										row,
										rowIdx,
										lastCol,
									)
								: columns.map((col, colIdx) =>
										this.renderCell(document.createElement('td'), row, rowIdx, col, colIdx),
									)}
						</tr>
					);
				}),
			);
		} else {
			tbody.innerHTML = '';
			if (this.showEmpty) {
				this.el.append(
					<div class="empty">
						<Empty />
					</div>,
				);
			}
		}
	}

	protected override renderHead() {
		const columns = this.getColumns();
		const thead = this.el.querySelector(':scope>table>thead');
		thead?.replaceChildren(
			this.headLine && columns.length > 0 ? (
				<tr class="head">
					{columns.map((col, colIdx) =>
						this.renderHeadCell(document.createElement('th'), col, colIdx),
					)}
				</tr>
			) : (
				''
			),
			<tr
				style={{
					height: '0',
					overflow: 'hidden',
				}}
			>
				{columns.map(() => (
					<td
						style={{
							boxSizing: 'border-box',
							padding: '0',
							margin: '0',
							height: '0',
							overflow: 'hidden',
						}}
					/>
				))}
			</tr>,
		);

		const colGroup = this.el.querySelector('colgroup');
		colGroup?.replaceChildren(...columns.map(() => <col />));
	}
}
